home *** CD-ROM | disk | FTP | other *** search
/ PC Format (PL) 2008 February / PC_Format_022008.iso / Internet / Mozilla Thunderbird wtyczki / lightning-0.7-tb-win.xpi / chrome / lightning.jar / content / lightning / agenda-tree.js < prev    next >
Encoding:
JavaScript  |  2007-10-05  |  20.6 KB  |  706 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Lightning code.
  15.  *
  16.  * The Initial Developer of the Original Code is Oracle Corporation
  17.  * Portions created by the Initial Developer are Copyright (C) 2005
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Mike Shaver <shaver@mozilla.org>
  22.  *   Vladimir Vukicevic <vladimir@pobox.com>
  23.  *   Dan Mosedale <dmose@mozilla.org>
  24.  *   Joey Minta <jminta@gmail.com>
  25.  *   Stefan Sitter <ssitter@googlemail.com>
  26.  *   Daniel Boelzle <daniel.boelzle@sun.com>
  27.  *   gekacheka@yahoo.com
  28.  *   richard@duif.net
  29.  *   Matthew Willis <mattwillis@gmail.com>
  30.  *   Markus Adrario <MarkusAdrario@web.de>
  31.  *   Philipp Kewisch <mozilla@kewis.ch>
  32.  *   Martin Schroeder <mschroeder@mozilla.x-home.org>
  33.  *
  34.  * Alternatively, the contents of this file may be used under the terms of
  35.  * either the GNU General Public License Version 2 or later (the "GPL"), or 
  36.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  37.  * in which case the provisions of the GPL or the LGPL are applicable instead
  38.  * of those above. If you wish to allow use of your version of this file only
  39.  * under the terms of either the GPL or the LGPL, and not to allow others to
  40.  * use your version of this file under the terms of the MPL, indicate your
  41.  * decision by deleting the provisions above and replace them with the notice
  42.  * and other provisions required by the GPL or the LGPL. If you do not delete
  43.  * the provisions above, a recipient may use your version of this file under
  44.  * the terms of any one of the MPL, the GPL or the LGPL.
  45.  *
  46.  * ***** END LICENSE BLOCK ***** */
  47.  
  48. // Agenda tree view, to display upcoming events, tasks, and reminders
  49. //
  50. // We track three periods of time for a segmented view:
  51. // - today: the current time until midnight
  52. // - tomorrow: midnight to midnight
  53. // - soon: end-of-tomorrow to end-of-one-week-from-today (midnight)
  54. //
  55. // Events (recurrences of events, really) are stored in per-period containers,
  56. // hung off of "period" objects. In addition, we build an array of the row-
  57. // representation we use for backing the tree display.
  58. //
  59. // The tree-view array (this.events) consists of the synthetic events for the time
  60. // periods, each one followed, if tree-expanded, by its collection of events.  This
  61. // results in a this.events array like the following, if "Today" and "Soon" are
  62. // expanded:
  63. // [ synthetic("Today"),
  64. //   occurrence("Today Event 1"),
  65. //   occurrence("Today Event 2"),
  66. //   synthetic("Tomorrow"),
  67. //   synthetic("Soon"),
  68. //   occurrence("Soon Event 1"),
  69. //   occurrence("Soon Event 2") ]
  70. //
  71. // At window load, we connect the view to the tree and initiate a calendar query
  72. // to populate the event buckets.  Once the query is complete, we sort each bucket
  73. // and then build the aggregate array described above.
  74. //
  75. // When calendar queries are refreshed (by a calendar being added/removed WRT the
  76. // current view, the user selecting a different filter, or some hidden manual-
  77. // refresh testing UI) the event buckets are emptied, and we add items as they
  78. // arrive.
  79. //
  80.  
  81. function Synthetic(title, open)
  82. {
  83.     this.title = title;
  84.     this.open = open;
  85.     this.events = [];
  86. }
  87.  
  88. var agendaTreeView = {
  89.     events: [],
  90.     todayCount: 0,
  91.     tomorrowCount: 0,
  92.     soonCount: 0,
  93.     prevRowCount: 0,
  94.     mListener: null,
  95.     initialized: false,
  96.     refreshQueue: [],
  97.     pendingRefresh: null
  98. };
  99.  
  100. agendaTreeView.init =
  101. function initAgendaTree()
  102. {
  103.     this.today = new Synthetic(ltnGetString("lightning", "agendaToday"), true);
  104.     this.tomorrow = new Synthetic(ltnGetString("lightning", "agendaTomorrow"), false);
  105.     this.soon = new Synthetic(ltnGetString("lightning", "agendaSoon"), false);
  106.     this.periods = [this.today, this.tomorrow, this.soon];
  107.     this.initialized = true;
  108. }
  109.  
  110. agendaTreeView.addEvents =
  111. function addEvents(master)
  112. {
  113.     this.events.push(master);
  114.     if (master.open)
  115.         this.events = this.events.concat(master.events);
  116. };
  117.  
  118. agendaTreeView.rebuildEventsArray =
  119. function rebuildEventsArray()
  120. {
  121.     this.events = [];
  122.     this.addEvents(this.today);
  123.     this.addEvents(this.tomorrow);
  124.     this.addEvents(this.soon);
  125. };
  126.  
  127. agendaTreeView.forceTreeRebuild =
  128. function forceTreeRebuild()
  129. {
  130.     if (this.tree) {
  131.         this.tree.view = this;
  132.     }
  133. };
  134.  
  135. agendaTreeView.rebuildAgendaView =
  136. function rebuildAgendaView(invalidate)
  137. {
  138.     this.rebuildEventsArray();
  139.     this.forceTreeRebuild();
  140. };
  141.  
  142. agendaTreeView.__defineGetter__("rowCount",
  143. function get_rowCount()
  144. {
  145.     return this.events.length;
  146. });
  147.  
  148. agendaTreeView.isContainer =
  149. function isContainer(row)
  150. {
  151.     return (this.events[row] instanceof Synthetic);
  152. };
  153.  
  154. agendaTreeView.isContainerOpen =
  155. function isContainerOpen(row)
  156. {
  157.     var open = this.events[row].open;
  158.     return open;
  159. };
  160.  
  161. agendaTreeView.isContainerEmpty =
  162. function isContainerEmpty(row)
  163. {
  164.     if (this.events[row].events.length == 0)
  165.         return true;
  166.     return false;
  167. };
  168.  
  169. agendaTreeView.setTree =
  170. function setTree(tree)
  171. {
  172.     this.tree = tree;
  173. };
  174.  
  175. agendaTreeView.showsToday =
  176. function showsToday()
  177. {
  178.   return (sameDay(today(), this.today.start));
  179. };
  180.  
  181. agendaTreeView.getCellText =
  182. function getCellText(row, column)
  183. {
  184.     var dateFormatter = Components.classes["@mozilla.org/calendar/datetime-formatter;1"]
  185.                                   .getService(Components.interfaces.calIDateTimeFormatter);
  186.     // title column
  187.     var event = this.events[row];
  188.     if (column.id == "col-agenda-item") {
  189.         if (event instanceof Synthetic) {
  190.             if (this.showsToday()) {
  191.                 return event.title;
  192.             }
  193.             else {
  194.                 if (event == this.today) {
  195.                     return dateFormatter.formatDate(this.today.start);
  196.                 }
  197.                 else if (event == this.tomorrow) {
  198.                     return dateFormatter.formatDate(this.tomorrow.start);
  199.                 }
  200.                 else {
  201.                     var startString = new Object();
  202.                     var endString = new Object();
  203.                     dateFormatter.formatInterval(
  204.                         this.soon.start, this.soon.end, startString, endString);
  205.                     var dateString = startString.value + " - " + endString.value;
  206.                     return dateString;
  207.                 }
  208.             }
  209.         }
  210.         else {
  211.             return event.title;
  212.         }
  213.     }
  214.     else {
  215.         if (event instanceof Synthetic) {
  216.             return "";
  217.         } else {
  218.             var start = event.startDate || event.dueDate || event.entryDate;
  219.             start = start.getInTimezone(calendarDefaultTimezone());
  220.             if (start.compare(this.tomorrow.end) == -1) {
  221.                 // time only for events on today and tomorrow
  222.                 return  dateFormatter.formatTime(start);
  223.             }
  224.             else {
  225.                 return dateFormatter.formatDateTime(start);
  226.             }
  227.         }
  228.     }
  229. };
  230.  
  231. agendaTreeView.getLevel =
  232. function getLevel(row)
  233. {
  234.     if (this.isContainer(row))
  235.         return 0;
  236.     return 1;
  237. };
  238.  
  239. agendaTreeView.isSorted =
  240. function isSorted() { return false; };
  241.  
  242. agendaTreeView.isEditable =
  243. function isEditable(row, column) { return false; };
  244.  
  245. agendaTreeView.isSeparator =
  246. function isSeparator(row) { return false; };
  247.  
  248. agendaTreeView.getImageSrc =
  249. function getImageSrc(row, column) { return null; };
  250.  
  251. agendaTreeView.getCellProperties =
  252. function getCellProperties(row, column, props) {
  253.     if (!this.isContainer(row)) {
  254.         return;
  255.     }
  256.  
  257.     var atomSvc = Components.classes["@mozilla.org/atom-service;1"]
  258.                             .getService(Components.interfaces.nsIAtomService);
  259.     props.AppendElement(atomSvc.getAtom("agenda-header"));
  260. };
  261.  
  262. agendaTreeView.getRowProperties =
  263. function getRowProperties(row) { return null; };
  264.  
  265. agendaTreeView.getColumnProperties =
  266. function getColumnProperties(column) { return null; };
  267.  
  268. agendaTreeView.cycleHeader =
  269. function cycleHeader(header)
  270. {
  271.     this.refreshCalendarQuery(); // temporary hackishness
  272.     this.rebuildAgendaView();
  273.     this.forceTreeRebuild();
  274. };
  275.  
  276. agendaTreeView.getParentIndex =
  277. function getParentIndex(row)
  278. {
  279.     if (this.isContainer(row))
  280.         return -1;
  281.     var i = row - 1;
  282.     do {
  283.         if (this.events[i] instanceof Synthetic)
  284.             return i;
  285.         i--;
  286.     } while (i != -1);
  287.     throw "no parent for row " + row + "?";
  288. };
  289.  
  290. agendaTreeView.toggleOpenState =
  291. function toggleOpenState(row)
  292. {
  293.     if (!this.isContainer(row))
  294.         throw "toggling open state on non-container row " + row + "?";
  295.     var header = this.events[row];
  296.     if (!("open") in header)
  297.         throw "no open state found on container row " + row + "?";
  298.     header.open = !header.open;
  299.     this.rebuildAgendaView(); // reconstruct the visible row set
  300.     this.forceTreeRebuild();
  301. };
  302.  
  303. agendaTreeView.hasNextSibling =
  304. function hasNextSibling(row, afterIndex)
  305. {
  306. };
  307.  
  308. agendaTreeView.findPeriodForItem =
  309. function findPeriodForItem(item)
  310. {
  311.     var start = item.startDate || item.entryDate || item.dueDate;
  312.     if (!start) 
  313.         return null;
  314.     start = start.getInTimezone(calendarDefaultTimezone());
  315.     if (start.compare(this.today.end) == -1)
  316.         return this.today;
  317.         
  318.     if (start.compare(this.tomorrow.end) == -1)
  319.         return this.tomorrow;
  320.     
  321.     if (start.compare(this.soon.end) == -1)
  322.         return this.soon;
  323.     
  324.  
  325.     return null;
  326. };
  327.  
  328. agendaTreeView.addItem =
  329. function addItem(item)
  330. {
  331.     var when = this.findPeriodForItem(item);
  332.     if (!when) {
  333.         return;
  334.     }
  335.  
  336.     // Prevent showing items with the same id but on multiple calendars from
  337.     // appearing multiple times in the agenda.
  338.     var dupe;
  339.     for (var i = 0; i < when.events.length; i++) {
  340.         if (item.id == when.events[i].id) {
  341.             when.events.splice(i, 1, item);
  342.             dupe = true;
  343.             return;
  344.         }
  345.         dupe = false;
  346.     }
  347.     if (!dupe) {
  348.         when.events.push(item);
  349.     }
  350.     this.calendarUpdateComplete();
  351. };
  352.  
  353. agendaTreeView.onDoubleClick =
  354. function agendaDoubleClick(event)
  355. {
  356.     // We only care about left-clicks
  357.     if (event.button != 0) 
  358.         return;
  359.  
  360.     // Find the row clicked on, and the corresponding event
  361.     var tree = document.getElementById("agenda-tree");
  362.     var row = tree.treeBoxObject.getRowAt(event.clientX, event.clientY);
  363.     var calendar = getSelectedCalendar();
  364.     var calEvent = this.events[row];
  365.  
  366.     if (!calEvent) { // Clicked in empty space, just create a new event
  367.         createEventWithDialog(calendar, this.today.start);
  368.         return;
  369.     }
  370.     if (!this.isContainer(row)) { // Clicked on a task/event, edit it
  371.         var eventToEdit = getOccurrenceOrParent(calEvent);
  372.         modifyEventWithDialog(eventToEdit);
  373.     } else { // Clicked on a container, create an event that day
  374.         if (calEvent == this.today) {
  375.             createEventWithDialog(calendar, this.today.start);
  376.         } else {
  377.             var tom = this.today.start.clone();
  378.             var offset = (calEvent == this.tomorrow) ? 1 : 2;
  379.             tom.day += offset;
  380.             createEventWithDialog(calendar, tom);
  381.         }
  382.     }
  383. }
  384.  
  385. agendaTreeView.onKeyPress =
  386. function onKeyPress(event)
  387. {
  388.   const kKE = Components.interfaces.nsIDOMKeyEvent;
  389.   switch(event.keyCode) {
  390.     case kKE.DOM_VK_DELETE:
  391.       document.getElementById('agenda_delete_event_command').doCommand();
  392.       event.stopPropagation();
  393.       event.preventDefault();
  394.       break;
  395.   }
  396. };
  397.  
  398. /**
  399.  *  Delete the current selected item with focus from the Agenda- list
  400.  */
  401. agendaTreeView.deleteEvent = 
  402. function deleteAgendaEvent(aDoNotConfirm)
  403. {
  404.    var selectedItems = new Array();
  405.    var tree = document.getElementById("agenda-tree");
  406.    var start = new Object();
  407.    var end = new Object();
  408.    var numRanges = tree.view.selection.getRangeCount();
  409.    var agendaItem;
  410.    for (var t = 0; t < numRanges; t++) {
  411.       tree.view.selection.getRangeAt(t, start, end);
  412.       for (var v = start.value; v <= end.value; v++) {
  413.         selectedItems.push(this.events[v]);
  414.       }
  415.    }
  416.    calendarViewController.deleteOccurrences(selectedItems.length,
  417.                                             selectedItems,
  418.                                             false,
  419.                                             aDoNotConfirm);
  420. };
  421.  
  422.  
  423. agendaTreeView.deleteItem =
  424. function deleteItem(item)
  425. {
  426.     var when = this.findPeriodForItem(item);
  427.     if (!when) {
  428.         return;
  429.     }
  430.     
  431.     when.events = when.events.filter(function (e) {
  432.                                          if (e.id != item.id)
  433.                                              return true;
  434.                                          if (e.recurrenceId && item.recurrenceId &&
  435.                                              e.recurrenceId.compare(item.recurrenceId) != 0)
  436.                                              return true;
  437.                                          return false;
  438.                                      });
  439.     this.rebuildAgendaView(true);
  440. };
  441.  
  442. agendaTreeView.calendarUpdateComplete =
  443. function calendarUpdateComplete()
  444. {
  445.     [this.today, this.tomorrow, this.soon].forEach(function(when) {
  446.         function compare(a, b) {
  447.             // The assumption is that tasks having a dueDate should be added
  448.             // to the agenda based on _that_, rather than entryDate, but tasks
  449.             // with an entryDate but no dueDate shouldn't be left out.
  450.             var ad = a.startDate || a.dueDate || a.entryDate;
  451.             var bd = b.startDate || b.dueDate || b.entryDate;
  452.             return ad.compare(bd);
  453.         }
  454.         when.events.sort(compare);
  455.     });
  456.     this.rebuildAgendaView(true);
  457. };
  458.  
  459. agendaTreeView.calendarOpListener =
  460. {
  461.     agendaTreeView: agendaTreeView
  462. };
  463.  
  464. agendaTreeView.calendarOpListener.onOperationComplete =
  465. function listener_onOperationComplete(calendar, status, optype, id,
  466.                                       detail)
  467. {
  468.     this.agendaTreeView.calendarUpdateComplete();
  469.  
  470.     // signal that the current operation finished.
  471.     this.agendaTreeView.pendingRefresh = null;
  472.  
  473.     // immediately start the next job on the queue.
  474.     this.agendaTreeView.popRefreshQueue();
  475. };
  476.  
  477. agendaTreeView.calendarOpListener.onGetResult =
  478. function listener_onGetResult(calendar, status, itemtype, detail, count, items)
  479. {
  480.     if (!Components.isSuccessCode(status))
  481.         return;
  482.  
  483.     items.forEach(this.agendaTreeView.addItem, this.agendaTreeView);
  484. };
  485.  
  486. agendaTreeView.popRefreshQueue =
  487. function popRefreshQueue()
  488. {
  489.     var pendingRefresh = this.pendingRefresh;
  490.     if (pendingRefresh) {
  491.         if (pendingRefresh instanceof Components.interfaces.calIOperation) {
  492.             this.pendingRefresh = null;
  493.             pendingRefresh.cancel(null);
  494.         } else {
  495.             return;
  496.         }
  497.     }
  498.  
  499.     var refreshJob = this.refreshQueue.pop();
  500.     if (!refreshJob) {
  501.         return;
  502.     }
  503.  
  504.     var filter = this.calendar.ITEM_FILTER_CLASS_OCCURRENCES;
  505.     if (document.getElementById("hide-completed-checkbox").checked) {
  506.         filter |= this.calendar.ITEM_FILTER_COMPLETED_NO;
  507.     } else {
  508.         filter |= this.calendar.ITEM_FILTER_COMPLETED_ALL;
  509.     }
  510.  
  511.     if (!this.filterType)
  512.         this.filterType = 'all';
  513.     switch (this.filterType) {
  514.         case 'all': 
  515.             filter |= this.calendar.ITEM_FILTER_TYPE_EVENT |
  516.                       this.calendar.ITEM_FILTER_TYPE_TODO;
  517.             break;
  518.         case 'events':
  519.             filter |= this.calendar.ITEM_FILTER_TYPE_EVENT;
  520.             break;
  521.         case 'tasks':
  522.             filter |= this.calendar.ITEM_FILTER_TYPE_TODO;
  523.             break;
  524.     }
  525.  
  526.     this.pendingRefresh = true;
  527.     this.periods.forEach(function (p) { p.events = []; });
  528.     pendingRefresh = this.calendar.getItems(filter, 0, this.today.start, this.soon.end,
  529.                                             this.calendarOpListener);
  530.     if (pendingRefresh && pendingRefresh.isPending) { // support for calIOperation
  531.         this.pendingRefresh = pendingRefresh;
  532.     }
  533. };
  534.  
  535. agendaTreeView.refreshCalendarQuery =
  536. function refreshCalendarQuery()
  537. {
  538.     var refreshJob = {};
  539.     this.refreshQueue.push(refreshJob);
  540.     this.popRefreshQueue();
  541. };
  542.  
  543. agendaTreeView.updateFilter =
  544. function updateAgendaFilter(menulist) {
  545.     this.filterType = menulist.selectedItem.value;
  546.     this.refreshCalendarQuery();
  547.     return;
  548. };
  549.  
  550. agendaTreeView.refreshPeriodDates =
  551. function refreshPeriodDates(d)
  552. {
  553.     // Today: now until midnight of tonight
  554.     this.today.start = d.clone();
  555.     d.hour = d.minute = d.second = 0;
  556.     d.day++;
  557.     this.today.end = d.clone();
  558.  
  559.     // Tomorrow: midnight of next day to +24 hrs
  560.     this.tomorrow.start = d.clone();
  561.     d.day++;
  562.     this.tomorrow.end = d.clone();
  563.  
  564.     // Soon: end of tomorrow to 6 six days later (remainder of the week period)
  565.     this.soon.start = d.clone();
  566.     d.day += 6;
  567.     this.soon.end = d.clone();
  568.  
  569.     this.refreshCalendarQuery();
  570. };
  571.  
  572. agendaTreeView.calendarObserver = {
  573.     agendaTreeView: agendaTreeView
  574. };
  575.  
  576. agendaTreeView.calendarObserver.QueryInterface = function agenda_QI(aIID) {
  577.     if (!aIID.equals(Components.interfaces.calIObserver) &&
  578.         !aIID.equals(Components.interfaces.calICompositeObserver) &&
  579.         !aIID.equals(Components.interfaces.nsISupports)) {
  580.         throw Components.results.NS_ERROR_NO_INTERFACE;
  581.     }
  582.     return this;
  583. };
  584.  
  585. // calIObserver:
  586. agendaTreeView.calendarObserver.onStartBatch = function agenda_onBatchStart() {
  587.     this.mBatchCount++;
  588. };
  589. agendaTreeView.calendarObserver.onEndBatch = function() {
  590.     this.mBatchCount--;
  591.     if (this.mBatchCount == 0) {
  592.         // Rebuild everything
  593.         this.agendaTreeView.refreshCalendarQuery();
  594.     }
  595. };
  596. agendaTreeView.calendarObserver.onLoad = function(calendar) {
  597.     this.agendaTreeView.refreshCalendarQuery();
  598. };
  599.  
  600. agendaTreeView.calendarObserver.onAddItem =
  601. function observer_onAddItem(item)
  602. {
  603.     if (this.mBatchCount) {
  604.         return;
  605.     }
  606.  
  607.     if (isToDo(item)) {
  608.         var hideCompleted = document.getElementById("hide-completed-checkbox").checked;
  609.         if (item.isCompleted && hideCompleted) {
  610.             return;
  611.         }
  612.     }
  613.  
  614.     var occs = item.getOccurrencesBetween(this.agendaTreeView.today.start,
  615.                                           this.agendaTreeView.soon.end, {});
  616.     occs.forEach(this.agendaTreeView.addItem, this.agendaTreeView);
  617.     this.agendaTreeView.rebuildAgendaView();
  618. };
  619.  
  620. agendaTreeView.calendarObserver.onDeleteItem =
  621. function observer_onDeleteItem(item, rebuildFlag)
  622. {
  623.     if (this.mBatchCount) {
  624.         return;
  625.     }
  626.     var queryStart = this.agendaTreeView.today.start.clone();
  627.     queryStart.hour = 0;
  628.     queryStart.minute = 0;
  629.     queryStart.second = 0;
  630.     var occs = item.getOccurrencesBetween(queryStart,
  631.                                           this.agendaTreeView.soon.end, {});
  632.     occs.forEach(this.agendaTreeView.deleteItem, this.agendaTreeView);
  633.     if (rebuildFlag != "no-rebuild")
  634.         this.agendaTreeView.rebuildAgendaView();
  635. };
  636.  
  637. agendaTreeView.calendarObserver.onModifyItem =
  638. function observer_onModifyItem(newItem, oldItem)
  639. {
  640.     if (this.mBatchCount) {
  641.         return;
  642.     }
  643.     this.onDeleteItem(oldItem, "no-rebuild");
  644.  
  645.     if (isToDo(newItem)) {
  646.         var hideCompleted = document.getElementById("hide-completed-checkbox").checked;
  647.         if (newItem.isCompleted && hideCompleted) {
  648.             return;
  649.         }
  650.     }
  651.     this.onAddItem(newItem);
  652. };
  653.  
  654. agendaTreeView.calendarObserver.onError = function(errno, msg) {};
  655.  
  656. agendaTreeView.calendarObserver.onCalendarAdded = 
  657. function agenda_calAdd(aCalendar) {
  658.     this.agendaTreeView.refreshCalendarQuery();
  659. };
  660.  
  661. agendaTreeView.calendarObserver.onCalendarRemoved = 
  662. function agenda_calRemove(aCalendar) {
  663.     this.agendaTreeView.refreshCalendarQuery();
  664. };
  665.  
  666. agendaTreeView.calendarObserver.onDefaultCalendarChanged = function(aCalendar) {
  667. };
  668.  
  669. agendaTreeView.setCalendar =
  670. function setCalendar(calendar)
  671. {
  672.     if (this.calendar)
  673.         this.calendar.removeObserver(this.calendarObserver);
  674.     this.calendar = calendar;
  675.     calendar.addObserver(this.calendarObserver);
  676.     this.init();
  677. };
  678.  
  679.  
  680. agendaTreeView.addListener = 
  681. function addListener(aListener) {
  682.     this.mListener = aListener;
  683. }
  684.  
  685. function setAgendaTreeView()
  686. {
  687.     agendaTreeView.setCalendar(getCompositeCalendar());
  688.     document.getElementById("agenda-tree").view = agendaTreeView;
  689.     if (agendaTreeView.mListener){
  690.       agendaTreeView.mListener.updatePeriod();
  691.     }    
  692. }
  693.  
  694. function sameDay(date1, date2) {
  695.    if (date1 && date2) {
  696.        if ((date1.day == date2.day) &&
  697.           (date1.month == date2.month) &&
  698.           (date1.year == date2.year)) {
  699.           return true;
  700.        }
  701.    }
  702.    return false;
  703.  }
  704.  
  705. window.addEventListener("load", setAgendaTreeView, false);
  706.